package br.com.sisgerenciamento.utilidades;


import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;

import br.com.sisgerenciamento.generic.BuilderHibernateSession;

/**
 * Utilitario para criacao de HQL
 */
public class BuildHQL extends BuilderHibernateSession{

	private static Logger LOG = Logger.getLogger(BuildHQL.class);
	private StringBuilder select;
	private Map<String, Object> params;
	private List<String> conditions;
	private String hqlOrdem;
	
	private Condition condition = Condition.AND;
	
	private Collection<BuildHQL> ors;
	
	public static BuildHQL novo(String hql) {
		return new BuildHQL(hql);
	}

	private BuildHQL(String hql) {
		this();
		this.select = new StringBuilder(hql);
	}

	public BuildHQL() {
		params = new HashMap<String, Object>(0);
		conditions = new ArrayList<String>(0);
		select = new StringBuilder();
	}

	public BuildHQL join(String join) {
		select.append(" ");
		select.append(join);
		return this;
	}

	public BuildHQL or() {
		if (ors == null) {
			ors = new ArrayList<BuildHQL>(1);
		}
		BuildHQL or = new BuildHQL();
		or.condition = Condition.OR;
		ors.add(or);
		return or;
	}
	
	public BuildHQL eq(String field, String nameParam, Object value) {
		if (isIgnoreValue(value)) {
			return this;
		}
		params.put(nameParam, value);
		conditions.add(field + " = :" + nameParam);
		return this;
	}

	public <T> BuildHQL in(String field, String nameParam, T... params) {
		StringBuilder in = new StringBuilder();
		for (int i = 0; i < params.length; i++) {

			T tipo = params[i];
			if (isIgnoreValue(tipo)) {
				return this;
			}
			if (in.length() > 0) {
				in.append(",");
			}
			in.append(":" + nameParam + i);
			this.params.put(nameParam + i, tipo);
		}
		if (in.length() > 0) {
			in.insert(0, "(");
			in.insert(in.length(), ")");
			conditions.add(field + " in " + in.toString());
		}
		return this;
	}
	
	public <T> BuildHQL notIn(String field, String nameParam, T... params) {
		StringBuilder in = new StringBuilder();
		for (int i = 0; i < params.length; i++) {

			T tipo = params[i];
			if (isIgnoreValue(tipo)) {
				return this;
			}
			if (in.length() > 0) {
				in.append(",");
			}
			in.append(":" + nameParam + i);
			this.params.put(nameParam + i, tipo);
		}
		if (in.length() > 0) {
			in.insert(0, "(");
			in.insert(in.length(), ")");
			conditions.add(field + " not in " + in.toString());
		}
		return this;
	}

	public BuildHQL sort(String field, TypeSort sort) {
		hqlOrdem = " order by " + field + " " + (sort.is(TypeSort.DESC)? "desc" : "asc");
		return this;
	}

	public String hql() {
		StringBuilder hql = new StringBuilder(select);
		if (!conditions.isEmpty()) {
			if (condition.is(Condition.AND)) {
				hql.append(" WHERE ");
			}
			for (int i = 0; i < conditions.size(); i++) {
				String condicao = conditions.get(i);
				if (i > 0) {
					hql.append(" AND ");
				}
				hql.append(condicao);
			}
		}
		if (ors != null) {
			hql.append(" OR (");
			for (BuildHQL or : ors) {
				hql.append(or.hql());
				params.putAll(or.params);
			}
			hql.append(")");
		}
		if (TreatString.isNotBlank(hqlOrdem)) {
			hql.append(this.hqlOrdem);
		}
		LOG.debug("HQL GERADO [" + hql + "]");
		return hql.toString();
	}

	public Query build() throws HibernateException, Exception {
		org.hibernate.Query query = getSessionAberta().createQuery(hql());
		return to(query);
	}

	/**
	 * Adiciona os parametros na query passada
	 */
	public Query to(org.hibernate.Query query) throws HibernateException, Exception {
		for (String paramName : params.keySet()) {
			query.setParameter(paramName, params.get(paramName));
		}
		return query;
	}
	
	public BuildHQL ilike(String field, String nameParm, String value) {
		if (isIgnoreValue(value)) {
			return this;
		}
		String like = "%" + value + "%";
		conditions.add("UPPER(" + TreatString.traduzirColuna(field) + ") like :" + nameParm);
		params.put(nameParm, TreatString.removerAcentos(like.toUpperCase()));
		return this;
	}
	
	public BuildHQL like(String field, String nameParm, String value) {
		if (isIgnoreValue(value)) {
			return this;
		}
		String like = value;
		conditions.add("UPPER(" + TreatString.traduzirColuna(field) + ") like :" + nameParm);
		params.put(nameParm, TreatString.removerAcentos(like.toUpperCase()));
		return this;
	}

	public BuildHQL isNotNull(String... fields) {
		for (String field : fields) {
			conditions.add(field + " is not null");
		}
		return this;
	}

	public BuildHQL isNotNull(Boolean add, String... string) {
		if (add) {
			this.isNotNull(string);
		}
		return this;
	}

	public BuildHQL isNotEmpty(Boolean add, String... fields) {
		if (!add) {
			return this;
		}
		for (String field : fields) {
			conditions.add(field + " is not empty");
		}
		return this;
	}
	
	public BuildHQL greaterOrEq(String field, String nameParam, Object value) {
		if (isIgnoreValue(value)) {
			return this;
		}
		params.put(nameParam, value);
		conditions.add(field + " >= :" + nameParam);
		return this;
	}
	
	public BuildHQL lessOrEq(String field, String nameParam, Object value) {
		if (isIgnoreValue(value)) {
			return this;
		}
		params.put(nameParam, value);
		conditions.add(field + " <= :" + nameParam);
		return this;
	}
	
	private Boolean isIgnoreValue(Object value) {
		if (value == null) {
			return true;
		}
		if (value instanceof Number) {
			return TreatNumber.isNullOrZero((Number) value);
		}
		return TreatString.isBlank(value.toString());
	}
	
	enum Condition {
		AND, OR;
		Boolean is(Condition type) {
			return this == type;
		}
	}
}

enum TypeSort {
	ASC, DESC;

	Boolean is(TypeSort type) {
		return this == type;
	}
};